package spring.cloud.tasks.api.zookeeper.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;
import spring.cloud.tasks.api.utils.CuratorFrameworkUtils;
import spring.cloud.tasks.api.zookeeper.TransactionalZooKeeper;
import spring.cloud.tasks.api.zookeeper.ZooKeeper;

import java.nio.charset.StandardCharsets;
import java.util.List;

@Slf4j
class ZooKeeperImpl implements ZooKeeper {

    private final CuratorFramework curatorFramework;

    public ZooKeeperImpl(CuratorFramework curatorFramework) {
        this.curatorFramework = curatorFramework;
    }

    @Override
    public boolean checkExists(final String path) {
        try {
            return null != curatorFramework.checkExists().forPath(path);
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public String getData(final String path) {
        try {
            if (checkExists(path)) {
                byte[] nodeData = curatorFramework.getData().forPath(path);
                if (nodeData == null) {// executor的分片可能存在全部飘走的情况，sharding节点有可能获取到的是null，需要对null做判断，否则new
                    // String时会报空指针异常
                    return null;
                }
                return new String(nodeData, StandardCharsets.UTF_8);
            } else {
                return null;
            }
        } catch (final KeeperException.NoNodeException ignore) {
            return null;
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public List<String> getChildren(final String path) {
        try {
            return curatorFramework.getChildren().forPath(path);
            // CHECKSTYLE:OFF
        } catch (final KeeperException.NoNodeException ignore) {
            return null;
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public void create(final String path) {
        create(path, "");
    }

    @Override
    public void create(final String path, Object value) {
        if (value == null) {
            log.warn("node value is null, won't create, node: {}", path);
            return;
        }
        try {
            curatorFramework.create().creatingParentsIfNeeded().forPath(path, value.toString().getBytes(StandardCharsets.UTF_8));
        } catch (final KeeperException.NodeExistsException ignore) {
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public void update(final String path, final Object value) {
        if (value == null) {
            log.info("node value is null, won't update, node: {}", path);
            return;
        }
        try {
            if (this.checkExists(path)) {
                curatorFramework.inTransaction().check().forPath(path).and().setData().forPath(path, value.toString().getBytes(StandardCharsets.UTF_8)).and().commit();
            } else {
                this.create(path, value);
            }
        } catch (final KeeperException.NoNodeException ignore) {
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public void delete(final String path) {
        try {
            if (null != curatorFramework.checkExists().forPath(path)) {
                curatorFramework.delete().forPath(path);
            }
        } catch (final KeeperException.NoNodeException ignore) {
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public void deleteRecursive(final String path) {
        try {
            if (null != curatorFramework.checkExists().forPath(path)) {
                CuratorFrameworkUtils.deleteRecursive(curatorFramework, path);
            }
        } catch (final KeeperException.NoNodeException ignore) {
            // CHECKSTYLE:OFF
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    /**
     * 如果节点不存在则填充节点数据.
     *
     * @param path  作业节点名称
     * @param value 作业节点数据值
     */
    @Override
    public void createIfNotExist(final String path, final Object value) {
        if (value == null) {
            log.info("node value is null, won't fillJobNodeIfNotExist, node: {}", path);
            return;
        }
        if (!checkExists(path)) {
            try {
                curatorFramework.create().creatingParentsIfNeeded().forPath(path, value.toString().getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    @Override
    public Stat getStat(String path) {
        try {
            return curatorFramework.checkExists().forPath(path);
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public long getMtime(String path) {
        try {
            Stat stat = curatorFramework.checkExists().forPath(path);
            if (stat != null) {
                return stat.getMtime();
            } else {
                return 0L;
            }
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public long getCtime(String path) {
        try {
            Stat stat = curatorFramework.checkExists().forPath(path);
            if (stat != null) {
                return stat.getCtime();
            } else {
                return 0L;
            }
        } catch (final Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    /**
     * 默认会check根路径
     */
    @Override
    public TransactionalZooKeeper useTransaction() {
        try {
            return new TransactionalZooKeeperImpl(curatorFramework);
        } catch (Exception ex) {
            // CHECKSTYLE:ON
            throw new RuntimeException(ex);
        }
    }

    @Override
    public CuratorFramework getCuratorFramework() {
        return curatorFramework;
    }

}
